package cn.innosmart.aq.util;

import android.content.Context;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.support.annotation.NonNull;
import android.util.Log;

import java.io.InputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

/**
 * Created by David on 2015/7/21.
 */
public class LanSearchUtil {
    private static class LanSearchUtilInstance {
        private static final LanSearchUtil instance = new LanSearchUtil();
    }

    public static LanSearchUtil getInstance() {
        return LanSearchUtilInstance.instance;
    }

    private LanSearchUtil() {

    }

    // 标志：当前是否正在搜索
    private boolean mSearchThreadRunning = false;
    // 用于接收gateway TCP 连接的服务器线程
    private TCPServerRunnable mSearchThread = null;

    // 接收TCP连接的端口
    private int SEARCH_TCP_LISTEN_PORT = 13000;
    // TCP监听超时时间 10 秒
    private int SEARCH_TCP_ACCEPT_TIMEOUT = 3000;
    // TCP读取数据超时时间 10 秒
    private int SEARCH_TCP_READ_TIMEOUT = 3000;
    // 用于记录搜索到的所有Palals
    private ArrayList<String> mSearchResult = null;

    private Context mContext;

    public void search(Context ctx, SearchResultInterface searchResultInterface) {
        this.searchResult = searchResultInterface;
        this.mContext = ctx;
        logw("searchPalals()");
        if (true == mSearchThreadRunning) {
            // 再次发送一次广播
            new Thread(new Runnable() {
                @Override
                public void run() {
                    sendSearchGatewayBroadcast();
                }
            }).start();
        } else {
            mSearchThreadRunning = true;
            // step1: 清理上次搜寻结果
            if (null == mSearchResult)
                mSearchResult = new ArrayList<String>();
            else
                mSearchResult.clear();
            // step2: 启动TCP Server，等待 gateway 的TCP连接
            mSearchThread = new TCPServerRunnable();
            new Thread(mSearchThread).start();
            // step3: 发送广播，把自己的IP：PORT广播出去
            new Thread(new Runnable() {
                @Override
                public void run() {
                    sendSearchGatewayBroadcast();
                }
            }).start();
        }
    }

    private String searchUid = null;
    private boolean isSearchUid;

    public void search(Context ctx, String uid, SearchResultInterface searchResultInterface) {
        this.searchResult = searchResultInterface;
        this.searchUid = uid;
        isSearchUid = true;
        this.mContext = ctx;
        logw("searchPalals()");
        if (true == mSearchThreadRunning) {
            // 再次发送一次广播
            new Thread(new Runnable() {
                @Override
                public void run() {
                    sendSearchGatewayBroadcast();
                }
            }).start();
        } else {
            mSearchThreadRunning = true;
            // step1: 清理上次搜寻结果
            if (null == mSearchResult)
                mSearchResult = new ArrayList<String>();
            else
                mSearchResult.clear();
            // step2: 启动TCP Server，等待 gateway 的TCP连接
            mSearchThread = new TCPServerRunnable();
            new Thread(mSearchThread).start();
            // step3: 发送广播，把自己的IP：PORT广播出去
            new Thread(new Runnable() {
                @Override
                public void run() {
                    sendSearchGatewayBroadcast();
                }
            }).start();
        }
    }

    // 发送search广播的端口
    private int SEARCH_BROADCAST_GATEWAY_PORT = 12000;

    // send broadcast to search gateway
    private void sendSearchGatewayBroadcast() {
        logw("sendSearchGatewayBroadcast()");
        DatagramPacket sendPacket = null;
        DatagramSocket sendSocket = null;
        // 获取自己的IP地址
        String myIP = getWiFiIP(mContext);
        // 组装广播发出去的内容(关键字:IP:Port)
        String sendDate = myIP + ":" + SEARCH_TCP_LISTEN_PORT + ":" + "AQ";
        allowMulticast();
        // send
        try {
            sendSocket = new DatagramSocket(SEARCH_BROADCAST_GATEWAY_PORT);
            byte[] buffer = new byte[sendDate.length()];
            sendPacket = new DatagramPacket(buffer, sendDate.length());
            byte[] data = sendDate.getBytes();
            sendPacket.setData(data);
            sendPacket.setLength(data.length);
            sendPacket.setPort(SEARCH_BROADCAST_GATEWAY_PORT);
            InetAddress broadcastAddr;
            broadcastAddr = InetAddress.getByName("255.255.255.255");
            sendPacket.setAddress(broadcastAddr);
            sendSocket.send(sendPacket);
        } catch (Exception e) {
            e.printStackTrace();
            logw("Exception! " + e.getMessage());
        }

        if (null != sendSocket)
            sendSocket.close();
        releaseMulicast();
    }

    private WifiManager.MulticastLock multicastLock;

    private void allowMulticast() {
        WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
        multicastLock = wifiManager.createMulticastLock("multicast.test");
        multicastLock.acquire();
    }

    private void releaseMulicast() {
        if (null != multicastLock)
            multicastLock.release();
    }

    class TCPServerRunnable implements Runnable {
        @Override
        public void run() {
            ServerSocket serverSocket = null;
            try {
                Random random = new Random();
                SEARCH_TCP_LISTEN_PORT = random.nextInt(40000) + 20000;
                serverSocket = new ServerSocket(SEARCH_TCP_LISTEN_PORT);
                serverSocket.setSoTimeout(SEARCH_TCP_ACCEPT_TIMEOUT);
                // 等待多个Palals的TCP连接
                while (true) {
                    Socket socket = null;
                    socket = serverSocket.accept();
                    logw("get one gateway tcp connect");
                    // 到达这里，说明有连接接入，创建新线程，读取数据
                    new Thread(new TCPReadDataRunnable(socket)).start();
                }
            } catch (Exception e) {
                logw("exit TCP Listen thread");
            }
            mSearchThreadRunning = false;
            if (searchResult != null) {
                searchResult.onResultCallBack(1, mSearchResult);
            }
            isSearchUid=false;
        }
    }

    class TCPReadDataRunnable implements Runnable {
        private Socket socket;

        public TCPReadDataRunnable(Socket s) {
            socket = s;
        }

        @Override
        public void run() {
            try {
                logw("reading data from gateway...");
                //从Socket当中得到InputStream对象
                InputStream inputStream = socket.getInputStream();
                String ip = socket.getRemoteSocketAddress().toString();
                ip = splitIpAddress(ip);
                int size = -1;
                String result = null;
                byte[] data = new byte[128];
                // 带有超时判断的读取数据
                size = readWithTimeout(inputStream, data, SEARCH_TCP_READ_TIMEOUT);
                if (-1 != size) {
                    result = new String(data, 0, size);
                    System.out.println("result=" + result);
                    // 保存到搜索结果中
                    if (mSearchThreadRunning) {
                        if (searchResult != null) {
                            searchResult.onResultIPCallBack(result, ip);
                        }
                        mSearchResult.add(result);
                    }
                }
                socket.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 切割出IP地址
     * @param ip
     * @return
     */
    @NonNull
    private String splitIpAddress(String ip) {
        System.out.println("address=" + ip);
        String[] strarray=ip.split("\\:");
        String[] strarray2=strarray[0].split("/");
        ip=strarray2[1];
        ip="tcp://"+ip+":1883";
        return ip;
    }


    /**
     * 设置超时读取时间
     * @param inputStream
     * @param b
     * @param msec
     * @return
     */
    private int readWithTimeout(final InputStream inputStream, final byte[] b, final int msec) {
        logw("readWithTimeout");
        int readByte = -1;
        ExecutorService executor = Executors.newFixedThreadPool(2);
        Callable<Integer> readTask = new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                return inputStream.read(b, 0, b.length);
            }
        };
        Future<Integer> future = executor.submit(readTask);
        try {
            readByte = future.get(msec, TimeUnit.MILLISECONDS);
        } catch (Exception e) {
            logw("exception!!");
        }
        future.cancel(true);
        executor.shutdown();
        return readByte;
    }

    public static String getWiFiIP(Context ctx) {
        //获取wifi服务
        WifiManager wifiManager = (WifiManager) ctx.getSystemService(Context.WIFI_SERVICE);
        //判断wifi是否开启
        if (!wifiManager.isWifiEnabled()) {
            wifiManager.setWifiEnabled(true);
        }
        WifiInfo wifiInfo = wifiManager.getConnectionInfo();
        int ipAddress = wifiInfo.getIpAddress();
        String ip = intToIp(ipAddress);
        return ip;
    }

    private static String intToIp(int i) {
        return (i & 0xFF) + "." +
                ((i >> 8) & 0xFF) + "." +
                ((i >> 16) & 0xFF) + "." +
                (i >> 24 & 0xFF);
    }


    private String TAG = "LanSearchUtil";

    private void logw(String msg) {
        Log.w(TAG, msg);
    }


    private static SearchResultInterface searchResult;

    public static interface SearchResultInterface {
        void onResultCallBack(int ret, Object object);
        void onResultIPCallBack(String uid, String ip);
    }

}
